// Serial.cpp

#include "serial/Serial.h"
#include <string>
#include <cstring>
#include <sys/ioctl.h>
using namespace std;
serialPort::serialPort()
{
	fd = -1;
}

int serialPort::InitSerialPort(const char *COM_NAME, int WhichCom, int BaudRate, int DataBits, int StopBits, int ParityBit)
{
	char tty[100];
	sprintf(tty, "%s%d", COM_NAME, WhichCom);
	if (OpenPort(tty) == false)
	{
		return -1;
	}
	if (setup(BaudRate, 0, DataBits, StopBits, ParityBit))
	{
		return -1;
	}
	return 0;
}

bool serialPort::OpenPort(const char *dev)
{

	// char *_dev = new char[32];
	std::string dev_str = dev;
	// std::strncpy(_dev, dev_str.c_str(), dev_str.length());
	printf("open == %s\n", dev_str.c_str());
	fd = open(dev_str.c_str(), O_RDWR); //| O_NOCTTY | O_NDELAY
	if (-1 == fd)
	{
		perror("Can't Open Serial Port");
		return false;
	}

	int DTR_flag;
	DTR_flag = TIOCM_DTR;
	ioctl(fd, TIOCMBIS, &DTR_flag); // Set RTS pin
	printf("open == %s ok\n", dev_str.c_str());
	return true;
}

int serialPort::Open(const char *dev, int BaudRate)
{
	if (OpenPort(dev) == false)
	{
		printf("open err\n");
		return -1;
	}
	if (setup(BaudRate, 0, 8, 1, 'N'))
	{
		printf("set err\n");
		return -1;
	}
	return 0;
}
int serialPort::setup(int speed, int flow_ctrl, int databits, int stopbits, int parity)
{

	int i;
	int status;
	struct termios options;
	/*tcgetattr(fd,&options)得到与fd指向对象的相关参数，并将它们保存于options,该函数还可以测试配置是否正确，该串口是否可用等。若调用成功，函数返回值为0，若调用失败，函数返回值为1.
	 */
	if (tcgetattr(fd, &options) != 0)
	{
		perror("SetupSerial 1");
		return -1;
	}

	// 设置串口输入波特率和输出波特率

	cfsetospeed(&options, speed);

	// 修改控制模式，保证程序不会占用串口
	options.c_cflag |= CLOCAL;
	// 修改控制模式，使得能够从串口中读取输入数据
	options.c_cflag |= CREAD;

	// 设置数据流控制
	switch (flow_ctrl)
	{

	case 0: // 不使用流控制
		options.c_cflag &= ~CRTSCTS;
		break;
	case 1: // 使用硬件流控制
		options.c_cflag |= CRTSCTS;
		break;
	case 2: // 使用软件流控制
		options.c_cflag |= IXON | IXOFF | IXANY;
		break;
	}
	// 设置数据位
	// 屏蔽其他标志位
	options.c_cflag &= ~CSIZE;
	switch (databits)
	{
	case 5:
		options.c_cflag |= CS5;
		break;
	case 6:
		options.c_cflag |= CS6;
		break;
	case 7:
		options.c_cflag |= CS7;
		break;
	case 8:
		options.c_cflag |= CS8;
		break;
	default:
		fprintf(stderr, "Unsupported data size\n");
		return -1;
	}
	// 设置校验位
	switch (parity)
	{
	case 'n':
	case 'N': // 无奇偶校验位。
		options.c_cflag &= ~PARENB;
		options.c_iflag &= ~INPCK;
		break;
	case 'o':
	case 'O': // 设置为奇校验
		options.c_cflag |= (PARODD | PARENB);
		options.c_iflag |= INPCK;
		break;
	case 'e':
	case 'E': // 设置为偶校验
		options.c_cflag |= PARENB;
		options.c_cflag &= ~PARODD;
		options.c_iflag |= INPCK;
		break;
	case 's':
	case 'S': // 设置为空格
		options.c_cflag &= ~PARENB;
		options.c_cflag &= ~CSTOPB;
		break;
	default:
		fprintf(stderr, "Unsupported parity\n");
		return -1;
	}
	// 设置停止位
	switch (stopbits)
	{
	case 1:
		options.c_cflag &= ~CSTOPB;
		break;
	case 2:
		options.c_cflag |= CSTOPB;
		break;
	default:
		fprintf(stderr, "Unsupported stop bits\n");
		return -1;
	}

	// 修改输出模式，原始数据输出
	options.c_oflag &= ~OPOST;

	options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);

	// 0x0d 回车符CR
	// 0x11 ^Q VSTART字符
	// 0x13 ^S VSTOP字符
	// ICRNL 将输入的CR转换为NL
	// IXON 使起动/停止输出控制流起作用

	// 这行解决串口接收不到0x11, 0x0d, 0x13的问题
	options.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
	// options.c_lflag &= ~(ISIG | ICANON);

	// 设置等待时间和最小接收字符
	options.c_cc[VTIME] = 1; /* 读取一个字符等待1*(1/10)s */
	options.c_cc[VMIN] = 1;	 /* 读取字符的最少个数为1 */
	// 如果发生数据溢出，接收数据，但是不再读取 刷新收到的数据但是不读
	tcflush(fd, TCIFLUSH);

	// 激活配置 (将修改后的termios数据设置到串口中）
	if (tcsetattr(fd, TCSANOW, &options) != 0)
	{
		perror("com set error!\n");
		return -1;
	}
	return 0;
}

int serialPort::readBuffer(uint8_t *buffer, int size)
{
	return read(fd, buffer, size);
}
int serialPort::writeBuffer(uint8_t *buffer, int size)
{
	return write(fd, buffer, size);
}
uint8_t serialPort::getchar()
{
	uint8_t t;
	read(fd, &t, 1);
	return t;
}
void serialPort::ClosePort()
{
	close(fd);
}

int serialPort::ReadChar(uint8_t &cRecved)
{
	if (-1 == fd)
		return -1;
	return read(fd, &cRecved, 1);
}
int serialPort::ReadChar(char &cRecved)
{
	if (-1 == fd)
		return -1;
	return read(fd, &cRecved, 1);
}
